<template>
  <div>
    <canvas
      ref="canvas"
      :width="CANVAS_WIDTH"
      :height="CANVAS_HEIGHT"
      :class="$style.canvas"
      @mousedown="mouseDown"
      @mousemove="mouseMove"
      @mouseup="mouseUp"
      @touchstart="touchStart"
      @touchmove="touchMove"
      @touchend="touchEnd"
      @touchcancel="touchEnd"
    />
  </div>
</template>

<script>
import Vue from 'vue'

const DPR = window.devicePixelRatio || 1
const CANVAS_WIDTH = window.innerWidth * DPR
const CANVAS_HEIGHT = window.innerHeight * DPR
function getPoint(e, element) {
  const boundingClientRect = element.getBoundingClientRect()
  const position = e.targetTouches ? e.targetTouches[0] : e
  const point = {
    x: position.pageX - boundingClientRect.left,
    y: position.pageY - boundingClientRect.top
  }
  const computedStyle = window.getComputedStyle(element, null)
  const canvasSize = {
    width: parseFloat(computedStyle.width),
    height: parseFloat(computedStyle.height)
  }
  return {
    x: point.x / canvasSize.width * CANVAS_WIDTH,
    y: point.y / canvasSize.height * CANVAS_HEIGHT,
    timestamp: +new Date()
  }
}
export default {
  name: 'Sign',
  props: {
    color: {
      type: String,
      default: '#333333'
    },
    baseLineWidth: {
      type: Number,
      default: 6
    }
  },
  data () {
    return {
      CANVAS_WIDTH,
      CANVAS_HEIGHT,
      drawRange: undefined
    }
  },
  computed: {
    canvas () {
      return this.$refs.canvas
    }
  },
  mounted () {
    this.clear()
  },
  methods: {
    recordRange(point, radius) {
      radius += 3
      if (this.drawRange) {
        this.drawRange.minX = Math.min(this.drawRange.minX, Math.max(0, point.x - radius))
        this.drawRange.maxX = Math.max(this.drawRange.maxX, Math.min(CANVAS_WIDTH, point.x + radius))
        this.drawRange.minY = Math.min(this.drawRange.minY, Math.max(0, point.y - radius))
        this.drawRange.maxY = Math.max(this.drawRange.maxY, Math.min(CANVAS_HEIGHT, point.y + radius))
      } else {
        Vue.set(this, 'drawRange', {
          minX: point.x - radius,
          maxX: point.x + radius,
          minY: point.y - radius,
          maxY: point.y + radius
        })
      }
    },
    mouseDown(e) {
      this.mouseActive = true
      this.touchStart(e)
    },
    mouseMove(e) {
      if (this.mouseActive) this.touchMove(e)
    },
    mouseUp() {
      if (this.mouseActive) this.touchEnd()
    },
    touchStart (e) {
      const ctx = this.$refs.canvas.getContext('2d')
      const point = getPoint(e, this.$refs.canvas)
      this.endTimestamp && point.timestamp - this.endTimestamp > 69 && ctx.beginPath()
      ctx.globalAlpha = 0.5
      ctx.arc(point.x, point.y, this.baseLineWidth / 2, 0, Math.PI * 2, true)
      ctx.fillStyle = this.color
      ctx.fill()
      ctx.closePath()
      this.recordRange(point, this.baseLineWidth / 2)
      this.endTimestamp = 0
      this.prePoint = point
    },
    touchMove(e) {
      e.preventDefault()
      const point = getPoint(e, this.$refs.canvas)
      if (this.prePoint && this.endTimestamp === 0) {
        const ctx = this.$refs.canvas.getContext('2d')
        const lineLength = Math.sqrt(Math.pow(this.prePoint.x - point.x, 2) + Math.pow(this.prePoint.y - point.y, 2))
        let lineWidth = Math.floor(Math.min((point.timestamp - this.prePoint.timestamp) / lineLength * 5, 9)) + this.baseLineWidth
        if (lineWidth > this.prePoint.lineWidth) {
          lineWidth = this.prePoint.lineWidth + 1
        } else if (lineWidth < this.prePoint.lineWidth) {
          lineWidth = this.prePoint.lineWidth - 1
        }
        point.lineWidth = lineWidth
        ctx.save()
        ctx.beginPath()
        ctx.fillStyle = this.color
        const pointCount = Math.ceil(lineLength / 2)
        for (let i = 0; i < pointCount; i++) {
          const x = this.prePoint.x + (point.x - this.prePoint.x) / pointCount * i
          const y = this.prePoint.y + (point.y - this.prePoint.y) / pointCount * i
          ctx.beginPath()
          ctx.arc(x, y, point.lineWidth / 2, 0, Math.PI * 2, true)
          ctx.fill()
        }
        ctx.arc(point.x, point.y, point.lineWidth / 6, 0, Math.PI * 2, true)
        ctx.fill()
        ctx.restore()
        this.recordRange(point, point.lineWidth / 6)
      }
      this.prePoint = point
    },
    touchEnd() {
      this.endTimestamp = +new Date()
    },
    clear() {
      this.$refs.canvas.getContext('2d').clearRect(0, 0, this.CANVAS_WIDTH, this.CANVAS_HEIGHT)
    },
    finish() {
      const canvas = document.createElement('canvas')
      const ctx = canvas.getContext('2d')
      canvas.width = this.drawRange.maxY - this.drawRange.minY
      canvas.height = this.drawRange.maxX - this.drawRange.minX
      ctx.save()
      ctx.rotate(-Math.PI / 2)
      ctx.drawImage(this.$refs.canvas, -this.drawRange.maxX, -this.drawRange.minY)
      ctx.restore()
      canvas.toBlob(async (blobObj) => {
        this.$emit('createSign', blobObj)
      })
    }
  }
}
</script>

<style module lang="scss">
.canvas{
  width: calc( 100vw - 80px);
  height: 100vh;
}
</style>
